package test6;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestFuture {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20, 3,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(20));

//        1 同步异步
//        Async 异步,不同线程(从线程池拿一条新线程执行任务) 当前线程可以继续执行其他任务，无需等待
//        Sync  同步,同一条线程(沿用原任务的线程) 需等待之前的任务完成才能继续
//
//        2 启动(首任务)
//        RunAsync 无返回结果
//        SupplyAsync 有返回结果
//
//        3 接力方法 (类比promise对象当中 then方法,即成功的回调)
//        ThenAccept 无返回值   仅消费结果，如打印、存储
//        ThenApply 有返回值    可转换结果，将结果映射为新值
//
//        4 辅助方法
//        whenComplete 线程收尾工作 参数是BiConsumer 无论成功与否都会执行,类似于finally块
//                     不会改变结果或处理异常,返回原结果的 CompletableFuture<T>的包装
//        Exceptionally 捕获异常 参数是Function 返回替代结果,类似于catch块
//                      返回新的 CompletableFuture,异常恢复,可替换异常结果为正常值
//        5 编排
//        CompletableFuture.allOf(c1,c2,c3,c4).join();插队,阻塞主线程,让主线程等待所有任务完成后继续执行
//        anyOf(a,b,c,d) 其中任意一个任务完成即触发
//        allOf(a,b,c,d) 等待所有任务都完成后触发

//todo         泛型可以写 Void ,API中Class<T>部分提到：
//            原始Java类型(boolean,byte,char,short,int,long,float和double)和关键字void也表示为类对象
//            T - 表示该类对象代表的具体类型  例如String.class的类型是Class<String>

        CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> {
//      Void.class是包装类 java.lang.Void 的类对象,Void 是一个不可实例化的占位符类，用于泛型中统一类型
            System.out.println("c1:"+Thread.currentThread().getName() + " is working...");
        }, threadPool);

        CompletableFuture<Integer> c2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("c2:"+Thread.currentThread().getName() + " is working...");
//            int i = 1 / 0;
            return 100;
        }, threadPool);

        CompletableFuture<Integer> cc = c2.whenCompleteAsync((data, throwable) -> {
            //  上游c2异常时获取不到data(因为c2还没return就中断了),data为null
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("cc:"+Thread.currentThread().getName() + " is working...");
            System.out.println("我是c2收尾线程,获得值：" + data);
        }, threadPool);  //todo 无论上游c2正常完成还是异常完成都会触发

//        CompletableFuture<Integer> c2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
//            @Override
//            public Integer get() {
//                System.out.println(Thread.currentThread().getName() + " is working...");
//                return 100;
//            }
//        }, threadPool); //匿名内部类写法

        CompletableFuture c3 = c2.thenAcceptAsync(i -> {
//todo      CompletableFuture<Void>后面的Void要手动删掉，否则会将c3的泛型规定为void
//         导致c3.exceptionally(Function<Throwable, ? extends T> fn)中的 T 为 void,表示无实际返回值,只能返回null
            System.out.println("c4:"+Thread.currentThread().getName() + " is working...");
            System.out.println(Thread.currentThread().getName() + " 接力c2, c2的值： " + i);
            int k = 1 / 0;
        }, threadPool);  //todo 仅在任务正常完成时触发(回调),上游c2异常时,不会执行,而是直接跳转到异常处理ce

//        以上问题更好的解决方法是使用thenApplyAsync,然后用c4.exceptionally
//        因为thenApplyAsync中return i(Integer);故c4的泛型为Integer,从而c4.exceptionally中可以返回Integer
        CompletableFuture<Integer> c4 = c2.thenApplyAsync(i -> {
            System.out.println("c3:" + Thread.currentThread().getName() + " is working...");
            System.out.println(Thread.currentThread().getName() + " 接力c2, c2的值： " + i);
            return i;
        }, threadPool);  //仅在任务正常完成时触发(回调),上游c2异常时,不会执行

        CompletableFuture ce = c3.exceptionally(throwable -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("ce:" + Thread.currentThread().getName() + "  上游出现异常  " + throwable);
            return 200;
        });


//        CompletableFuture.allOf(c1,c2,c3,c4,cc).join();
        CompletableFuture.allOf(c1,c2,c3,c4).join();
//       allOf返回一个新的 CompletableFuture<Void>,当所有传入的 CompletableFuture 都完成后,这个新 Future 才会完成
//       join()阻塞调用线程（此处是主线程）,直到 allOf 返回的 Future 完成后才继续执行后续代码

        System.out.println(Thread.currentThread().getName() + " is working...");

        threadPool.shutdownNow();
        
    }
}
